package be.rivendale.renderer;

import be.rivendale.geometry.AxisAlignedBoundingBox;
import be.rivendale.geometry.Camera;
import be.rivendale.geometry.TriangleModel;
import be.rivendale.ghetto.RayBoxIntersectionGhetto;
import be.rivendale.ghetto.Shader;
import be.rivendale.material.Color;
import be.rivendale.mathematics.Ray;
import be.rivendale.mathematics.Triangle;
import be.rivendale.mathematics.Vertex;
import be.rivendale.mathematics.intersection.Intersection;
import be.rivendale.mathematics.intersection.RayIntersectionMode;
import be.rivendale.renderer.mk2.data.Rectangle;
import be.rivendale.renderer.mk2.data.RenderInformation;

import java.awt.image.BufferedImage;
import java.util.concurrent.Callable;

public class RenderTask implements Callable<ScreenPart> {
    private OverlayRenderer overlayRenderer = new OverlayRenderer();
//    private static TriangleModel triangleModel = RawFileLoader.loadRawFile("./data/asteroid-lowres.raw.txt", new Triple(2, 0, 0));
//    private static TriangleModel triangleModel = RawFileLoader.loadRawFile("./data/torus.raw.txt", new Triple(2, 0, 0));

    public static RenderInformation info = new RenderInformation();
    private static TriangleModel triangleModel = info.getScene().getModel();
//    private static TriangleModel triangleModel = RawFileLoader.loadRawFile("./data/multiple.raw.txt", new Triple(0, 0, 0));
//    private static TriangleModel triangleModel = RawFileLoader.loadRawFile("./data/triangle.raw.txt", new Triple(0, 0, 0));
//    private static ExperimentalVoxelGrid voxelGrid = new ExperimentalVoxelGrid(triangleModel);
    private static AxisAlignedBoundingBox triangleModelBoundingBox = info.getScene().getModel().getAxisAlignedBoundingBox();
//    private static Model world = new Model(Arrays.asList(new Triangle(
//		new Vertex(1, -1, 1),
//		new Vertex(0, 1, 1),
//		new Vertex(-1, -1, 1)
//	)));

    private static Camera camera = info.getScene().getCamera();
    private static Vertex light = info.getScene().getLight();

    private Rectangle rectangle;

    public RenderTask(Rectangle rectangle) {
        this.rectangle = rectangle;
    }

    public ScreenPart call() throws Exception {
        ScreenPart screenPart = new ScreenPart(rectangle.getX(), rectangle.getY(), renderScreenPartAsImage());
        overlayRenderer.drawOverlay(screenPart);
        return screenPart;
	}

	private BufferedImage renderScreenPartAsImage() {
		BufferedImage image = new BufferedImage(rectangle.getWidth(), rectangle.getHeight(), BufferedImage.TYPE_INT_RGB);
		traceAllRays(image);
		return image;
	}

    private void traceAllRays(BufferedImage image) {
		for(int yCoordinate = 0; yCoordinate < rectangle.getHeight(); yCoordinate++) {
			traceScanLine(image, yCoordinate);
		}
	}

	private void traceScanLine(BufferedImage image, int yCoordinate) {
		for(int xCoordinate = 0; xCoordinate < rectangle.getWidth(); xCoordinate++) {
			Color color = traceRay(yCoordinate, xCoordinate);
			image.getRaster().setPixel(xCoordinate, yCoordinate, color.toArray());
		}
	}

	private Color traceRay(int yCoordinate, int xCoordinate) {
        Ray ray = camera.castRay(xCoordinate + rectangle.getX(), yCoordinate + rectangle.getY(), info.getHorizontalCameraResolution(), info.getVerticalCameraResolution());
//        Color color = voxelGrid.traverseVoxels(ray);
//        if(color != null) {
//            return color;
//        }

        if(RayBoxIntersectionGhetto.rayBoxIntersection(ray, triangleModelBoundingBox) == null) {
            return Color.BLACK;
        }
        for (Triangle triangle : triangleModel.getTriangles()) {
            //if(RayBoxIntersectionGhetto.rayBoxIntersection(ray, triangle.getAxisAlignedBoundingBox()) != null) {
                Intersection intersection = triangle.intersection(ray, RayIntersectionMode.afterPassThroughPoint);
                if(intersection.doesIntersect()) {
                    return calculateSampleColor(intersection, triangle);
                }
            //}

        }
        return Color.WHITE.multiply(0.1);
    }

    private Color calculateSampleColor(Intersection intersection, Triangle triangle) {
        return Shader.shade(light, triangle.normal().normalize().invert(), ((Vertex)triangle.getA()).getColor(), intersection.getIntersectionPoint());
    }
}

